Fix generator emitting invalid C# for nullable activity input types#763
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes a source-generator bug in Microsoft.DurableTask.Generators where class-based Durable Functions activity triggers with nullable input types (e.g., object?) were emitted with input = default ahead of required parameters, producing invalid C# (CS1737) and failing compilation.
Changes:
- Removed nullable-input defaulting (
= default) specifically from[ActivityTrigger]function signatures inAddActivityFunctionDeclaration. - Extended generator tests to cover
object?activity inputs and to assert the intended asymmetry betweenCall*Asynchelpers (may keep= default) and trigger functions (must not). - Added an Unreleased changelog entry documenting the fix and linking to #730.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/Generators/DurableTaskSourceGenerator.cs |
Stops appending = default to nullable activity trigger inputs to avoid optional-before-required parameters (CS1737). |
test/Generators.Tests/AzureFunctionsTests.cs |
Adds an object? test case and validates correct generated signatures for both helper and trigger methods. |
CHANGELOG.md |
Documents the generator fix in the Unreleased section with issue link. |
The source generator appended `= default` to the [ActivityTrigger] input parameter when an activity's input type was nullable (e.g. object?). Because that parameter is followed by the required instanceId and executionContext parameters, this produced an optional parameter ahead of required ones, which is invalid C# (CS1737) and failed to compile. The generated *CallAsync helpers legitimately keep `= default` because their input is followed by an optional options parameter, so only AddActivityFunctionDeclaration is changed. Extended the Activities_ClassBasedSyntax regression test with an object? case that reproduces the issue. Fixes #730 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
fa07be7 to
d7b7dcb
Compare
kaibocai
approved these changes
Jul 15, 2026
halspang
approved these changes
Jul 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #730.
Microsoft.DurableTask.Generatorsproduced code that does not compile when a class-based activity uses a nullable input type such asobject?. For example:generated:
The
= defaultmakesinputoptional, but it is followed by the requiredinstanceIdandexecutionContextparameters — an optional parameter cannot precede required ones (CS1737), so the generated file fails to compile.Root cause
AddActivityFunctionDeclarationinDurableTaskSourceGenerator.csappended= defaultto the[ActivityTrigger]parameter whenever the input type ended in?. Unlike the*CallAsynchelpers, the trigger parameter is followed by required parameters, so a default value is both invalid and unnecessary (the Functions runtime always binds the input).Fix
= defaultappend inAddActivityFunctionDeclarationonly, with a comment explaining why this method differs from the*CallAsynchelpers.= defaultsites (ScheduleNew…InstanceAsync,Call…Asyncsub-orchestrator/activity helpers) are intentionally left unchanged: their input is followed by an optionaloptionsparameter, soinput = default, options = nullis valid C#.Tests
Activities_ClassBasedSyntaxtheory with anobject?input case (added#nullable enableto the input snippet). The expected output verifies the asymmetry: theCallMyActivityAsynchelper keepsobject? input = default(valid — followed by optionaloptions), while the[Function]trigger emitsobject? input(no default).Generators.Testssuite: 89/89 passing.